#include <jni.h>
#include <string>
#include <android/log.h>

#include <phrtf.h>
#include <sndfile.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <filesystem>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

//定义TAG之后，我们可以在LogCat通过TAG过滤出NDK打印的日志
#define TAG "JNITEST"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)

int rows = 72;
int cols = 25;
int32_t **hrtfData44k = NULL;
int32_t **hrtfData48k = NULL;
int inputShift44k = 0;
int inputShift48k = 0;

extern "C"
JNIEXPORT void JNICALL
Java_com_mi_audio_phrtf_AudioDetect_nativeInit(JNIEnv *env, jobject thiz) {
    hrtfData44k = NULL;
    hrtfData48k = NULL;
    inputShift44k = 0;
    inputShift48k = 0;
}

float *byteArray2FloatArray(JNIEnv *env, jbyteArray l1, jint bitDepth) {// 获取数组长度
    // 获取 Java byte[] 数据
    jsize byteLength = env->GetArrayLength(l1);
    jbyte *byteBuffer = env->GetByteArrayElements(l1, NULL);

    // 计算样本数量
    int sampleCount = 0;
    switch (bitDepth) {
        case 8:
            sampleCount = byteLength;
            break;
        case 16:
            sampleCount = byteLength / 2;
            break;
        case 24:
            sampleCount = byteLength / 3;
            break;
        case 32:
            sampleCount = byteLength / 4;
            break;
        default:
            env->ReleaseByteArrayElements(l1, byteBuffer, JNI_ABORT);
            return 0; // 不支持的位深
    }

    // 分配 float* 数据
    float *floatBuffer = (float *) malloc(sampleCount * sizeof(float));
    if (!floatBuffer) {
        env->ReleaseByteArrayElements(l1, byteBuffer, JNI_ABORT);
        return 0; // 内存分配失败
    }

    // 转换 PCM 数据为 float
    for (int i = 0; i < sampleCount; i++) {
        int sample = 0;
        switch (bitDepth) {
            case 8:
                sample = (int) ((unsigned char) byteBuffer[i] - 128);
                floatBuffer[i] = sample / 128.0f;
                break;

            case 16:
                sample = (int) (byteBuffer[2 * i] | (byteBuffer[2 * i + 1] << 8));
                floatBuffer[i] = sample / 32768.0f;
                break;

            case 24:
                sample = (int) (byteBuffer[3 * i] | (byteBuffer[3 * i + 1] << 8) |
                                (byteBuffer[3 * i + 2] << 16));
                if (sample & 0x800000) sample |= ~0xFFFFFF; // 扩展负值
                floatBuffer[i] = sample / 8388608.0f;
                break;

            case 32:
                sample = (int) (byteBuffer[4 * i] | (byteBuffer[4 * i + 1] << 8) |
                                (byteBuffer[4 * i + 2] << 16) | (byteBuffer[4 * i + 3] << 24));
                floatBuffer[i] = sample / 2147483648.0f;
                break;
        }
    }

    // 释放 Java byte[] 的引用
    env->ReleaseByteArrayElements(l1, byteBuffer, JNI_ABORT);

    // 返回 float* 的地址作为 long
    return floatBuffer;
}


extern "C"
JNIEXPORT jint JNICALL
Java_com_mi_audio_phrtf_AudioDetect_nativeDetect(JNIEnv *env, jobject thiz, jbyteArray l1,
                                                 jbyteArray l2,
                                                 jbyteArray r1, jbyteArray r2, jbyteArray c1,
                                                 jbyteArray c2) {
    float *data_inL1 = byteArray2FloatArray(env, l1, 32);
    float *data_inL2 = byteArray2FloatArray(env, l2, 32);
    float *data_inR1 = byteArray2FloatArray(env, r1, 32);
    float *data_inR2 = byteArray2FloatArray(env, r2, 32);
    float *data_inC1 = byteArray2FloatArray(env, c1, 32);
    float *data_inC2 = byteArray2FloatArray(env, c2, 32);

    size_t size = sizeof(data_inL1) / sizeof(data_inL1[0]);

    LOGE("Float array contents:\n");
    for (size_t i = 0; i < size; i++) {
        LOGE("data[%zu] = %.2f\n", i, data_inL1[i]);
    }

    /*初始化*/
    void *phrtfPara = phrtf_init();
    int indexStamp = 0;


    /*错误检测
    输入1：phrtfPara 初始化后的结构体
    输入2：data_inL1, data_inL2, data_inC1, data_inC2, data_inR1, data_inR2为录到的6段音频 
    输出1：checkhrtf 错误检测返回值*/
    int checkhrtf = hrtfWavCheck(phrtfPara, data_inL1, data_inL2, data_inC1, data_inC2, data_inR1,
                                 data_inR2);

    /*如果错误检测通过返回111，进行个性化HRTF计算
    输入1：phrtfPara 初始化后的结构体
    输入2：data_inC1, data_inL1, data_inR1为三个方向录制到的左耳音频
    输出1：匹配的hrtf索引*/
    if (checkhrtf == 111) {
        indexStamp = phrtf_process(phrtfPara, data_inC1, data_inL1, data_inR1);
    }

    /*用个性化HRTF计算得出的indexStamp索引，输出数据库中两种采样率下的的hrtfData 2-D数组和inpuShift进行传输
    输入1：2D数组行列数，采样率，inputShift
    输入2：indexStamp 个性化HRTF计算得出的索引
    输出1：hrtfData44k，hrtfData48k为hrtf数据，
    输出2：inputShift44k， inputShift48k为inputShift*/
    hrtfData44k = allocateAndFill2DArray(rows, cols, indexStamp, 44100, &inputShift44k);
    hrtfData48k = allocateAndFill2DArray(rows, cols, indexStamp, 48000, &inputShift48k);

    LOGE("inputshift 44k %d 48k %d", inputShift44k, inputShift48k);

    /*内存释放*/
    phrtf_free(phrtfPara);
    freeAndFill2DArray(hrtfData44k, rows);
    freeAndFill2DArray(hrtfData48k, rows);

    return checkhrtf;
}

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_mi_audio_phrtf_AudioDetect_getHrtfData44k(JNIEnv *env, jobject thiz) {
    // 获取 int 数组的类引用
    jclass intArrayClass = env->FindClass("[I");
    if (intArrayClass == NULL) {
        // 类加载失败，返回 NULL
        return NULL;
    }

    // 创建一个 jobjectArray 表示二维数组（行数为 rows）
    jobjectArray result = env->NewObjectArray(rows, intArrayClass, NULL);
    if (result == NULL) {
        // 内存分配失败，返回 NULL
        return NULL;
    }

    // 填充每一行
    for (jint i = 0; i < rows; i++) {
        // 创建一维 jintArray（列数为 cols）
        jintArray intArray = env->NewIntArray(cols);
        if (intArray == NULL) {
            // 内存分配失败，返回 NULL
            return NULL;
        }

        // 创建并填充 C 数组，作为示例数据
        jint temp[cols];
        for (jint j = 0; j < cols; j++) {
            temp[j] = hrtfData44k[i][j]; // 示例数据，可根据实际需要进行更改
        }

        // 将 C 数组复制到 jintArray
        env->SetIntArrayRegion(intArray, 0, cols, temp);

        // 将填充好的 jintArray 设置为 jobjectArray 的元素
        env->SetObjectArrayElement(result, i, intArray);

        // 释放局部引用
        env->DeleteLocalRef(intArray);
    }

    return result;
}

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_mi_audio_phrtf_AudioDetect_getHrtfData48k(JNIEnv *env, jobject thiz) {
    // 获取 int 数组的类引用
    jclass intArrayClass = env->FindClass("[I");
    if (intArrayClass == NULL) {
        // 类加载失败，返回 NULL
        return NULL;
    }

    // 创建一个 jobjectArray 表示二维数组（行数为 rows）
    jobjectArray result = env->NewObjectArray(rows, intArrayClass, NULL);
    if (result == NULL) {
        // 内存分配失败，返回 NULL
        return NULL;
    }

    // 填充每一行
    for (jint i = 0; i < rows; i++) {
        // 创建一维 jintArray（列数为 cols）
        jintArray intArray = env->NewIntArray(cols);
        if (intArray == NULL) {
            // 内存分配失败，返回 NULL
            return NULL;
        }

        // 创建并填充 C 数组，作为示例数据
        jint temp[cols];
        for (jint j = 0; j < cols; j++) {
            temp[j] = hrtfData48k[i][j]; // 示例数据，可根据实际需要进行更改
        }

        // 将 C 数组复制到 jintArray
        env->SetIntArrayRegion(intArray, 0, cols, temp);

        // 将填充好的 jintArray 设置为 jobjectArray 的元素
        env->SetObjectArrayElement(result, i, intArray);

        // 释放局部引用
        env->DeleteLocalRef(intArray);
    }

    return result;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_mi_audio_phrtf_AudioDetect_getInputShift44k(JNIEnv *env, jobject thiz) {
    return inputShift44k;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_mi_audio_phrtf_AudioDetect_getInputShift48k(JNIEnv *env, jobject thiz) {
    return inputShift48k;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_mi_audio_phrtf_AudioDetect_nativeTest(JNIEnv *env, jobject thiz, jstring l1, jstring l2,
                                               jstring r1, jstring r2, jstring c1, jstring c2) {
}

//#define TXT

int
spatial_audio_phrtf(unsigned blocksize, const char *infilenameL1, const char *infilenameL2,
                    const char *infilenameR1,
                    const char *infilenameR2, const char *infilenameC1, const char *infilenameC2);

extern "C"
JNIEXPORT jint JNICALL
Java_com_mi_audio_phrtf_AudioDetect_detectFile(JNIEnv *env, jobject thiz, jstring l1, jstring l2,
                                               jstring r1, jstring r2, jstring c1, jstring c2) {
    unsigned blocksize = 1024;

    int result = 0;
    const char *infilenameL1 = env->GetStringUTFChars(l1, 0);
    const char *infilenameL2 = env->GetStringUTFChars(l2, 0);
    const char *infilenameR1 = env->GetStringUTFChars(r1, 0);
    const char *infilenameR2 = env->GetStringUTFChars(r2, 0);
    const char *infilenameC1 = env->GetStringUTFChars(c1, 0);
    const char *infilenameC2 = env->GetStringUTFChars(c2, 0);
    result = spatial_audio_phrtf(blocksize, infilenameL1, infilenameL2, infilenameR1, infilenameR2,
                                 infilenameC1, infilenameC2);

    return result;
}

//L1 = 45pL, L2 = 45pR, C1 = 0L, C2 = 0R, R1 = 45nL, R2 = 45nR
int
spatial_audio_phrtf(unsigned blocksize, const char *infilenameL1, const char *infilenameL2,
                    const char *infilenameR1, const char *infilenameR2, const char *infilenameC1,
                    const char *infilenameC2) {
    int checkhrtf = 000;
#ifndef TXT
    struct tm tp;            // tm??????????
    time_t now;                // ????????time_t??????????????
    time(&now);                // ??????????????????????????
//	localtime_s(&tp, &now); // ??????????????????????????????


    SNDFILE *infileL1;
    SF_INFO infileinfoL1;
    memset(&infileinfoL1, 0, sizeof(infileinfoL1));
    SNDFILE *infileL2;
    SF_INFO infileinfoL2;
    memset(&infileinfoL2, 0, sizeof(infileinfoL2));
    SNDFILE *infileR1;
    SF_INFO infileinfoR1;
    memset(&infileinfoR1, 0, sizeof(infileinfoR1));
    SNDFILE *infileR2;
    SF_INFO infileinfoR2;
    memset(&infileinfoR2, 0, sizeof(infileinfoR2));
    SNDFILE *infileC1;
    SF_INFO infileinfoC1;
    memset(&infileinfoC1, 0, sizeof(infileinfoC1));
    SNDFILE *infileC2;
    SF_INFO infileinfoC2;
    memset(&infileinfoC2, 0, sizeof(infileinfoC2));

    if (!(infileL1 = sf_open(infilenameL1, SFM_READ, &infileinfoL1)) ||
        !(infileL2 = sf_open(infilenameL2, SFM_READ, &infileinfoL2)) ||
        !(infileR1 = sf_open(infilenameR1, SFM_READ, &infileinfoR1)) ||
        !(infileR2 = sf_open(infilenameR2, SFM_READ, &infileinfoR2)) ||
        !(infileC1 = sf_open(infilenameC1, SFM_READ, &infileinfoC1)) ||
        !(infileC2 = sf_open(infilenameC2, SFM_READ, &infileinfoC2))) {
        printf("Error: Unable to open input file. \n");
        puts(sf_strerror(NULL));
        return 0;
    };

    // spatialAudio initialize
    int fs = infileinfoL1.samplerate;
    int channelIn = infileinfoL1.channels;

    time_t start, finish;
    void *phrtfPara = NULL;
    // audio duration 
    int T1 = fs * 2.3;
    int T2 = fs * 2.3;
    int T3 = fs * 2.3;

    if ((infileinfoL1.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
        (infileinfoL2.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
        (infileinfoR1.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
        (infileinfoR2.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
        (infileinfoC1.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
        (infileinfoC2.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) { // 32bit
        // Allocate memory for data_in, L1 = 45pL, L2 = 45pR, C1 = 0L, C2 = 0R, R1 = 45nL, R2 = 45nR
        float *data_inL1 = static_cast<float *>(calloc(T1 * infileinfoL1.channels,
                                                       sizeof(*data_inL1)));
        float *data_inL2 = static_cast<float *>(calloc(T1 * infileinfoL2.channels,
                                                       sizeof(*data_inL2)));
        float *data_inR1 = static_cast<float *>(calloc(T2 * infileinfoR1.channels,
                                                       sizeof(*data_inR1)));
        float *data_inR2 = static_cast<float *>(calloc(T2 * infileinfoR2.channels,
                                                       sizeof(*data_inR2)));
        float *data_inC1 = static_cast<float *>(calloc(T3 * infileinfoC1.channels,
                                                       sizeof(*data_inC1)));
        float *data_inC2 = static_cast<float *>(calloc(T3 * infileinfoC2.channels,
                                                       sizeof(*data_inC2)));

        if (data_inL1 == NULL || data_inL2 == NULL || data_inR1 == NULL || data_inR2 == NULL ||
            data_inC1 == NULL || data_inC2 == NULL) {
            LOGE("Error: Cannot allocate memory for data_in.\n");
            return 0;
        }
        // left channel and right channel data buffer

        if ((infileinfoL1.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
            (infileinfoL2.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
            (infileinfoR1.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
            (infileinfoR2.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
            (infileinfoC1.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ||
            (infileinfoC2.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
            phrtfPara = phrtf_init();
        } else {
            return 0;
        }
        unsigned int n;
        unsigned long long cnt = 1;
        unsigned int random_int = 0;
        srand(0);

        // Process data
        start = clock();
        int16_t counter = 0;
        sf_read_float(infileL1, data_inL1, T1);
        sf_read_float(infileR1, data_inR1, T2);
        sf_read_float(infileC1, data_inC1, T3);
        sf_read_float(infileL2, data_inL2, T1);
        sf_read_float(infileR2, data_inR2, T2);
        sf_read_float(infileC2, data_inC2, T3);

        int indexStamp = 0;
        checkhrtf = hrtfWavCheck(phrtfPara, data_inL1, data_inL2, data_inC1, data_inC2, data_inR1,
                                 data_inR2);
        if (checkhrtf == 111) {
            indexStamp = phrtf_process(phrtfPara, data_inC1, data_inL1, data_inR1);
        }
        hrtfData44k = allocateAndFill2DArray(rows, cols, indexStamp, 44100, &inputShift44k);
        hrtfData48k = allocateAndFill2DArray(rows, cols, indexStamp, 48000, &inputShift48k);
        phrtf_free(phrtfPara);
        finish = clock();

        LOGE("inputshift 44k %d 48k %d", inputShift44k, inputShift48k);

        freeAndFill2DArray(hrtfData44k, rows);
        freeAndFill2DArray(hrtfData48k, rows);
        // Process terminated, free memory
        free(data_inL1);
        free(data_inL2);
        free(data_inR1);
        free(data_inR2);
        free(data_inC1);
        free(data_inC2);
        LOGE("%4.2f seconds passed. Finish!\n", (float) (finish - start) / CLOCKS_PER_SEC);
    } else {
        LOGE("Error: Input file is not 16 or 32 bit wav.\n");
    }
    sf_close(infileL1);
    sf_close(infileL2);
    sf_close(infileR1);
    sf_close(infileR2);
    sf_close(infileC1);
    sf_close(infileC2);
#else


    FILE* infileL1 = fopen(infilenameL1, "r");
    FILE* infileL2 = fopen(infilenameL2, "r");
    FILE* infileR1 = fopen(infilenameR1, "r");
    FILE* infileR2 = fopen(infilenameR2, "r");
    FILE* infileC1 = fopen(infilenameC1, "r");
    FILE* infileC2 = fopen(infilenameC2, "r");
    if (infileL1 == NULL || infileL2 == NULL || infileR1 == NULL || infileR2 == NULL || infileC1 == NULL || infileC2 == NULL) {
        LOGE("Unable to open the txt file.\n");
        return 0;
    }
    int fs = 48000;
    int T = fs * 2.3;
    float* data_inL1 = static_cast<float *>(calloc(T * 1, sizeof(*data_inL1)));
    float* data_inL2 = static_cast<float *>(calloc(T * 1, sizeof(*data_inL2)));
    float* data_inR1 = static_cast<float *>(calloc(T * 1, sizeof(*data_inR1)));
    float* data_inR2 = static_cast<float *>(calloc(T * 1, sizeof(*data_inR2)));
    float* data_inC1 = static_cast<float *>(calloc(T * 1, sizeof(*data_inC1)));
    float* data_inC2 = static_cast<float *>(calloc(T * 1, sizeof(*data_inC2)));

    for (int i = 0; i < T; i++) {
        fscanf((FILE *) infileL1, "%f", &data_inL1[i]);
        fscanf((FILE *) infileL2, "%f", &data_inL2[i]);
        fscanf((FILE *) infileR1, "%f", &data_inR1[i]);
        fscanf((FILE *) infileR2, "%f", &data_inR2[i]);
        fscanf((FILE *) infileC1, "%f", &data_inC1[i]);
        fscanf((FILE *) infileC2, "%f", &data_inC2[i]);
    }

    size_t size = sizeof(data_inL1) / sizeof(data_inL1[0]);

    // 打印指针所指向的值
    LOGE("Value: %f\n", *data_inL1);

    LOGE("Float array contents:\n");
    for (size_t i = 0; i < size; i++) {
        LOGE("data[%zu] = %.2f\n", i, data_inL1[i]);
    }

    void* phrtfPara = NULL;
    phrtfPara = phrtf_init();
    int indexStamp = 0;
    checkhrtf = hrtfWavCheck(phrtfPara, data_inL1, data_inL2, data_inC1, data_inC2, data_inR1, data_inR2);
    if (checkhrtf == 111)
    {
        indexStamp = phrtf_process(phrtfPara, data_inC1, data_inL1, data_inR1);
    }
    hrtfData44k = allocateAndFill2DArray(rows, cols, indexStamp, 44100, &inputShift44k);
    hrtfData48k = allocateAndFill2DArray(rows, cols, indexStamp, 48000, &inputShift48k);
    phrtf_free(phrtfPara);
    LOGE("inputshift 44k %d 48k %d", inputShift44k, inputShift48k);
    // Process terminated, free memory
    free(data_inL1);
    free(data_inL2);
    free(data_inR1);
    free(data_inR2);
    free(data_inC1);
    free(data_inC2);
#endif
    LOGE("checkresult %d", checkhrtf);
    return checkhrtf;
}

